// (C) 2021 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module ioc_top 
    #(
        parameter MASTER                 = 1,  //Set IOC Master side with value 1
        parameter BUS_SPEED_KHZ          = 400,
		  parameter NORMAL_GPIO_RST_VALUE  = 112'hFF_FF_FF_FF_FF_FF_FF_FF_FF_FF_FF_FF_FF_FF
     )
(
input                clk,          //Source Clock to drive ioc master/slave block. Should be input from master side.
input                rst_n,
output               tx_coreclock, //Clk from tx-lvds internal pll
output               tx_pll_locked,
output               aligned,      //Mark that LVDS link has locked
output reg           normal_gpio_stable, //All GPIO_OUT value has been updated with remote MCSI payload
    
input                lvds_clk_in,
output               lvds_clk_out,
input                lvds_rx_in,
output               lvds_tx_out,

inout      [11:0]    smb_scl,        //I2C interfaces tunneling through LVDS 
inout      [11:0]    smb_sda,

input      [111:0]   gpio_in,        //GPIO input tunneling through LVDS
output reg [111:0]   gpio_out,       //GPIO output tunneling through LVDS

input      [1:0]     uart_rxd,       //UART interfaces tunneling through LVDS
input      [1:0]     uart_cts,       //Clear To Send
output reg [1:0]     uart_txd,
output reg [1:0]     uart_rts,       //Request To Send

output     [15:0]    avmm_mst_addr,  //AVMM Master interface tunneling through LVDS
output               avmm_mst_read,  //Only exist on ioc_slave side
output               avmm_mst_write,
output     [31:0]    avmm_mst_wdata,
output     [ 3:0]    avmm_mst_byteen,
input      [31:0]    avmm_mst_rdata,
input                avmm_mst_rdvalid,
input                avmm_mst_waitrq,

input      [15:0]    avmm_slv_addr,  //AVMM Slave interface tunneling through LVDS
input                avmm_slv_read,  //Only exist on ioc_master side
input                avmm_slv_write,
input      [31:0]    avmm_slv_wdata,
input      [ 3:0]    avmm_slv_byteen,
output     [31:0]    avmm_slv_rdata,
output               avmm_slv_rdvalid,
output               avmm_slv_waitrq,

 
input      [15:0]    avmm_csr_addr,  //Standard AVMM interface for reading CSRs in IOC IP
input                avmm_csr_read,
input                avmm_csr_write,
input      [31:0]    avmm_csr_wdata,
input      [ 3:0]    avmm_csr_byteen,
output     [31:0]    avmm_csr_rdata,
output               avmm_csr_rdvalid,
output               avmm_csr_waitrq,

output     [3:0]     status_reg_mm,                  //LVDS_MM channel status
output               invalid_access_mcsi,            //IIC_MM access error report
input                invalid_access_cpu,             //Only exist on agent side, input to MM SM         
input                invalid_access_dbg              //Only exist on agent side, input to MM SM

);


wire [13:0]      relay_scl_i;
wire [13:0]      relay_sda_i;
wire [13:0]      relay_scl_en;
wire [13:0]      relay_sda_en;
wire [11:0]      i2c_channel_timeout;
wire [11:0][3:0] i2c_event_i_array;
wire [11:0][3:0] i2c_event_o_array;
reg  [ 1:0][3:0] uart_i_array;
wire [ 1:0][3:0] uart_o_array;
wire [7:0]       GPIO_out_8;  
wire [7:0]       frame_count;
wire [3:0]       tx_frm_offset;
wire [3:0]       rx_frm_offset;
wire [7:0]       frame_count_check; 
wire [1:0]       inject_frame_comma_err;
wire [1:0]       inject_frame_crc_err; 
wire             bist_mode_enable;
wire             frame_crc_err;
wire             bist_err;
wire             frame_comma_err;  
wire             soft_i2c_channel_rst;
wire             soft_avmm_channel_rst;
wire             mcsi_system_rst;
wire [111:0]     gpio_in_synced;
wire [  1:0]     uart_rxd_synced;
wire [  1:0]     uart_cts_synced;
wire             gpio_i_sample_en;
wire             gpio_o_sample_en;
wire             uart_i_sample_en;
wire             uart_o_sample_en;
wire [7:0]       avmm_event_o;   //lvds_mm evcoding events serialized across LVDS
wire [7:0]       avmm_event_i;
wire             avmm_channel_crc_err; 
wire             avmm_channel_timeout; 
wire             avmm_channel_cmpl_err;
wire [31:0]      avmm_crc_err_cnt;    
wire [31:0]      avmm_timeout_err_cnt;
wire [31:0]      avmm_cmpl_err_cnt;   

reg [ 7:0]       GPIO_in_8;
reg [ 1:0]       uart_rxd_synced_0; 
reg [ 1:0]       uart_rxd_synced_1;
reg [ 1:0]       uart_rxd_synced_2;
reg [ 7:0]       uart_tx_latch_cnt;
reg [ 7:0]       uart_rx_replay_cnt;

wire             frame_loss_crc_H2A;        //Indicates H2A direction has frame error
wire             frame_aligned_tx;          //indicates H2A aligned at host side or A2H aligned at agent side
wire             frame_loss_crc_A2H;        //Indicates A2H direction has frame error

        
genvar i;
generate 
for(i = 0; i < 12; i = i+1) begin : SMBus
    
assign smb_scl[i]     = relay_scl_en[i] ? 1'b0 : 1'bz;
assign relay_scl_i[i] = smb_scl[i];
assign smb_sda[i]     = relay_sda_en[i] ? 1'b0 : 1'bz;
assign relay_sda_i[i] = smb_sda[i];
    
    if(MASTER) begin
        smbus_relay_mst 
            #(
                .CLOCK_PERIOD_PS   ( 50000       ),                     // Period of the input 'clock', in picoseconds (default 10,000 ps = 100 MHz)
                .BUS_SPEED_KHZ     (BUS_SPEED_KHZ)                      // Bus speed of the slowest device on the slave SMBus interface, in kHz (must be 100, 400, or 1000)
             )         
        smbus_relay_mst_0 (
            .clock             ( tx_coreclock                           ),  // master clock for this block                                              
            .i_resetn          ( tx_pll_locked & !soft_i2c_channel_rst ),  // master reset, must be de-asserted synchronously with clock                                                                                                                     
            .ioc_frame_i       ( i2c_event_i_array[i]                   ),
            .ioc_frame_o       ( i2c_event_o_array[i]                   ),                                                                      
            .ia_master_scl     ( relay_scl_i[i]                         ),   // asynchronous input from the SCL pin of the master interface
            .o_master_scl_oe   ( relay_scl_en[i]                        ),   // when asserted, drive the SCL pin of the master interface low
            .ia_master_sda     ( relay_sda_i[i]                         ),   // asynchronous input from the SDA pin of the master interface
            .o_master_sda_oe   ( relay_sda_en[i]                        ),   // when asserted, drive the SDA pin of the master interface low
            .stretch_timeout   ( i2c_channel_timeout[i]                 )
        );
    end else begin
        smbus_relay_slv 
            #(
                .CLOCK_PERIOD_PS    ( 50000       ),       
                .BUS_SPEED_KHZ      (BUS_SPEED_KHZ)
             )          
        smbus_relay_slv_0 (
            .clock              ( tx_coreclock                           ),                                                             
            .i_resetn           ( tx_pll_locked & !soft_i2c_channel_rst ),                                                                             
            .ioc_frame_i        ( i2c_event_i_array[i]                   ),
            .ioc_frame_o        ( i2c_event_o_array[i]                   ),                                                                   
            .ia_master_scl      ( relay_scl_i[i]                         ),      
            .o_master_scl_oe    ( relay_scl_en[i]                        ),   
            .ia_master_sda      ( relay_sda_i[i]                         ),      
            .o_master_sda_oe    ( relay_sda_en[i]                        ),
            .stretch_timeout    ( i2c_channel_timeout[i]                 )            
        );
    end //if_MASTER
    
end//for
endgenerate


async_input_filter
    #(
        .NUM_METASTABILITY_REGS (2),
        .NUM_FILTER_REGS        (2)
     ) 
u_sync [111:0] (
    .clock                  ( tx_coreclock   ),
    .ia_async_in            ( gpio_in        ),
    .o_sync_out             ( gpio_in_synced ),
    .o_rising_edge          (                ),
    .o_falling_edge         (                )
);

async_input_filter
    #(
        .NUM_METASTABILITY_REGS (2),
        .NUM_FILTER_REGS        (2)
     ) 
uart_rxd_sync [1:0] (
    .clock                  ( tx_coreclock    ),
    .ia_async_in            ( uart_rxd        ),
    .o_sync_out             ( uart_rxd_synced ),
    .o_rising_edge          (                 ),
    .o_falling_edge         (                 )
);

async_input_filter
    #(
        .NUM_METASTABILITY_REGS (2),
        .NUM_FILTER_REGS        (2)
     ) 
uart_cts_sync [1:0] (
    .clock                  ( tx_coreclock    ),
    .ia_async_in            ( uart_cts        ),
    .o_sync_out             ( uart_cts_synced ),
    .o_rising_edge          (                 ),
    .o_falling_edge         (                 )
);

        
always @ (posedge tx_coreclock or negedge tx_pll_locked) begin
    if( !tx_pll_locked ) begin    
        GPIO_in_8 <= 8'h0;                                     //gpio_in_synced[(frame_count%14)*8+:8];
        if(MASTER) begin
            gpio_out <= NORMAL_GPIO_RST_VALUE;
        end else begin
            gpio_out <= NORMAL_GPIO_RST_VALUE;
        end        
    end else begin    
        if(gpio_i_sample_en) begin
            GPIO_in_8 <= gpio_in_synced[(frame_count%14)*8+:8];
        end else begin
            GPIO_in_8  <= GPIO_in_8;
        end

        if(gpio_o_sample_en & aligned) begin
            gpio_out[((frame_count_check)%14)*8+:8] <= GPIO_out_8;
        end else begin
            gpio_out   <= gpio_out;
        end
    end
end

 
//Normal Latency GPIO Stable Indication
//Mormal latency needs couple MCSI Frame to update the entire GPIO
//platform logic need to wait until normal_gpio_stable asserts before using GPIO_OUT
always @ ( posedge tx_coreclock or negedge tx_pll_locked ) begin
    if( !tx_pll_locked ) begin  
        normal_gpio_stable <= 1'b0;
    end else if ( frame_count == 8'h0 ) begin
	     normal_gpio_stable <= 1'b0;
	 end else if (frame_count > 8'he) begin
        normal_gpio_stable <= 1'b1;
    end
end


assign uart_i_sample_en = (uart_tx_latch_cnt%12 == 9);
assign uart_o_sample_en = (uart_rx_replay_cnt%12 == 0);


//uart
genvar j;
generate
for ( j = 0; j < 2; j = j+1 ) begin: UART

    always @ (posedge tx_coreclock or negedge tx_pll_locked) begin
        if(!tx_pll_locked) begin
            uart_i_array[j] <= 4'h7;
            uart_txd[j]     <= 1'b1;
            uart_rts[j]     <= 1'b0; //reset value for rts should be low value
        end else begin
            if(aligned)begin
                if(uart_i_sample_en)
                    uart_i_array[j] <= {uart_cts_synced[j],uart_rxd_synced_2[j],uart_rxd_synced_1[j],uart_rxd_synced_0[j]};
                if(uart_o_sample_en) begin
                    uart_txd[j] <= uart_o_array[j][0];
                    uart_rts[j] <= uart_o_array[j][3];
                end
                if(uart_rx_replay_cnt%12 == 4) begin
                    uart_txd[j] <= uart_o_array[j][1];
                end
                if(uart_rx_replay_cnt%12 == 8) begin
                    uart_txd[j] <= uart_o_array[j][2];
                end
            end else begin //when not aligned, send 4'h7 to the other side and output txd tp 1'b1 and rts to 1'b0
                uart_i_array[j] <= 4'h7;
                uart_txd[j]     <= 1'b1;
                uart_rts[j]     <= 1'b0; 
            end
        end
    end
    
end //for
endgenerate

always_ff @ (posedge tx_coreclock or negedge tx_pll_locked) begin
    if(!tx_pll_locked) begin
        uart_rxd_synced_0 <= 1'b1; //reset value should be 1 to as UART idle is pulled high, to avoid glitch.
        uart_rxd_synced_1 <= 1'b1;
        uart_rxd_synced_2 <= 1'b1;
    end else begin
        if ( uart_tx_latch_cnt%12 == 0 ) begin
            uart_rxd_synced_0 <= uart_rxd_synced;
        end else if ( uart_tx_latch_cnt%12 == 4 ) begin
            uart_rxd_synced_1 <= uart_rxd_synced;
        end else if ( uart_tx_latch_cnt%12 == 8 ) begin
            uart_rxd_synced_2 <= uart_rxd_synced;
        end else begin
            uart_rxd_synced_0 <= uart_rxd_synced_0;
            uart_rxd_synced_1 <= uart_rxd_synced_1;
            uart_rxd_synced_2 <= uart_rxd_synced_2;
        end
    end
end


//Can't use tx_frm_offset for latch uart_data directly to resolve glitch cause by sampling sequence. 
always_ff @ (posedge tx_coreclock or negedge tx_pll_locked) begin
    if(!tx_pll_locked) begin
        uart_tx_latch_cnt  <= 8'h0;
    end else begin
        if ( tx_frm_offset == 4'h0 ) begin
            uart_tx_latch_cnt <= 8'h0;
        end else begin
            uart_tx_latch_cnt <= uart_tx_latch_cnt + 8'b1;
        end
    end
end

//Can't use rx_frm_offset for replay uart_data directly to resolve glitch cause by sampling sequence. 
always_ff @ (posedge tx_coreclock or negedge tx_pll_locked) begin
    if(!tx_pll_locked) begin
        uart_rx_replay_cnt <= 8'h0;
    end else begin
        if ( rx_frm_offset == 4'h0 ) begin
            uart_rx_replay_cnt <= 8'h0;
        end else begin
            uart_rx_replay_cnt <= uart_rx_replay_cnt + 8'b1;
        end
    end
end

//Memory Mapped Channel over serial link
lvds_mm
    #(
    .MASTER        ( MASTER ),
    .DATA_WIDTH    ( 32     ),
    .ADDR_WIDTH    ( 16     ),
    .TIMEOUT_PARAM ( MASTER ? 42000 : 40000  )    //2.1ms for host side, 2ms for agent
    )
lvds_mm_inst
(
    .clk                         ( tx_coreclock                         ),
    .reset                       ( !tx_pll_locked||soft_avmm_channel_rst ),
    //Only exists on IOC master side
    .slv_agent_address           ( avmm_slv_addr                        ),//16 bits address
    .slv_agent_read              ( avmm_slv_read                        ),
    .slv_agent_write             ( avmm_slv_write                       ),
    .slv_agent_writedata         ( avmm_slv_wdata                       ),
    .slv_agent_byteenable        ( avmm_slv_byteen                      ),
    .slv_agent_readdata          ( avmm_slv_rdata                       ),
    .slv_agent_readdatavalid     ( avmm_slv_rdvalid                     ),
    .slv_agent_waitrequest       ( avmm_slv_waitrq                      ),
    //Only exists on IOC slave side                                     
    .mstr_agent_address          ( avmm_mst_addr                        ),
    .mstr_agent_read             ( avmm_mst_read                        ),
    .mstr_agent_write            ( avmm_mst_write                       ),
    .mstr_agent_writedata        ( avmm_mst_wdata                       ),
    .mstr_agent_byteenable       ( avmm_mst_byteen                      ),
    .mstr_agent_readdata         ( avmm_mst_rdata                       ),
    .mstr_agent_readdatavalid    ( avmm_mst_rdvalid                     ),
    .mstr_agent_waitrequest      ( avmm_mst_waitrq                      ),
    //LVDS-MM encoding events                                           
    .ioc_frame_o                 ( avmm_event_i                         ), //LVDS_MM 8-bit encoding events 
    .ioc_frame_i                 ( avmm_event_o                         ), //Cross Connection
    //tx/rx frame cnt
    .tx_frm_offset               ( tx_frm_offset                        ),
    .rx_frm_offset               ( rx_frm_offset                        ),
	 //input from lvds_channel_ctrl
	 .frame_loss_crc_A2H          ( frame_loss_crc_A2H                   ),
    .frame_aligned_tx            ( frame_aligned_tx                     ),
    .frame_loss_crc_H2A          ( frame_loss_crc_H2A                   ),
    //Register outputs                                                            
    .status_reg                  ( status_reg_mm                        ),
	 .avmm_timeout                ( avmm_channel_timeout                 ),
    .crc_check_pass              ( avmm_channel_crc_err                 ),
    .unexpected_cmpl             ( avmm_channel_cmpl_err                ),
    .timeout_cnt                 ( avmm_timeout_err_cnt                 ),
    .crc_err_cnt                 ( avmm_crc_err_cnt                     ),
    .unexpected_cmpl_cnt         ( avmm_cmpl_err_cnt                    ),
    .invalid_access_cpu          ( invalid_access_cpu                   ), //Only exist on agent side, input to MM SM
    .invalid_access_dbg          ( invalid_access_dbg                   )	//Only exist on agent side, input to MM SM 
);

//*************************** IOC Channel Contrl top *********************************
channel_ctrl_top
    #(
        .MASTER  ( MASTER )
     ) 
channel_ctrl_top_inst
(
    .clk                   ( clk                   ), 
    .rst_n                 ( rst_n                 ),           //synced reset from Tx_PLL locked 
    .mcsi_system_rstn      ( !mcsi_system_rst      ),    
    .tx_coreclock          ( tx_coreclock          ),
    .tx_pll_locked         ( tx_pll_locked         ),
    
    .lvds_clk_in           ( lvds_clk_in           ),
    .lvds_clk_out          ( lvds_clk_out          ),              
    .lvds_rx_in            ( lvds_rx_in            ),
    .lvds_tx_out           ( lvds_tx_out           ),   
                                               
    .i2c_event_i           ( i2c_event_o_array     ),
    .i2c_event_o           ( i2c_event_i_array     ),
                         
    .GPIO_i                ( GPIO_in_8             ),
    .GPIO_o                ( GPIO_out_8            ),
    
    .uart_i                ( uart_i_array          ),
    .uart_o                ( uart_o_array          ),
    
    .lvds_mm_i             ( avmm_event_i          ),
    .lvds_mm_o             ( avmm_event_o          ),
                                                        
    .frame_count           ( frame_count           ),
    .tx_frm_offset         ( tx_frm_offset         ),
    .rx_frm_offset         ( rx_frm_offset         ),
    .frame_count_check     ( frame_count_check     ),    
    .gpio_i_sample_en      ( gpio_i_sample_en      ),
    .gpio_o_sample_en      ( gpio_o_sample_en      ),    
                         
    .bist_mode_enable      ( bist_mode_enable      ),
	 .inject_frame_crc_err  ( inject_frame_crc_err  ),
	 .inject_frame_comma_err( inject_frame_comma_err),
    .frame_crc_err         ( frame_crc_err         ),
    .bist_err              ( bist_err              ),
    .frame_comma_err       ( frame_comma_err       ),
    .frame_loss_crc_A2H    ( frame_loss_crc_A2H    ),
    .frame_aligned_tx      ( frame_aligned_tx      ),
    .frame_loss_crc_H2A    ( frame_loss_crc_H2A    ),          
	 
    .aligned               ( aligned               )
);

mcsi_csr   mcsi_csr_inst
(
    .clk                   ( tx_coreclock          ),
    .rst_n                 ( tx_pll_locked         ), //Should use lock from tx-lvds internal pll
    //AVMM Intf
    .avmm_addr             ( avmm_csr_addr         ),
    .avmm_read             ( avmm_csr_read         ),
    .avmm_write            ( avmm_csr_write        ),
    .avmm_wdata            ( avmm_csr_wdata        ),
    .avmm_byteen           ( avmm_csr_byteen       ),
    .avmm_rdvalid          ( avmm_csr_rdvalid      ), 
    .avmm_waitrq           ( avmm_csr_waitrq       ),
    .avmm_rdata            ( avmm_csr_rdata        ),
    //Input SB signals
    .mcsi_link_aligned     ( aligned               ),
    .frame_crc_err         ( frame_crc_err         ),
	 .frame_comma_err       ( frame_comma_err       ),
    .avmm_channel_crc_err  ( avmm_channel_crc_err  ),
    .avmm_channel_timeout  ( avmm_channel_timeout  ),
    .avmm_channel_cmpl_err ( avmm_channel_cmpl_err ),
    .i2c_channel_timeout   ( |i2c_channel_timeout  ),
    .bist_err              ( bist_err              ),
    .tx_frm_offset         ( tx_frm_offset         ),	 
    //Input counter      
    .avmm_crc_err_cnt      ( avmm_crc_err_cnt      ),
    .avmm_timeout_err_cnt  ( avmm_timeout_err_cnt  ),
    .avmm_cmpl_err_cnt     ( avmm_cmpl_err_cnt     ),
    //Output SB signals
    .mcsi_system_rst       ( mcsi_system_rst       ),
    .i2c_channel_rst       ( soft_i2c_channel_rst  ),
    .avmm_channel_rst      ( soft_avmm_channel_rst ),
	 .inject_frame_crc_err  ( inject_frame_crc_err  ),
	 .inject_frame_comma_err( inject_frame_comma_err),
    .bist_mode_enable      ( bist_mode_enable      ),
    .invalid_access        ( invalid_access_mcsi   )    
);    
                                     
endmodule
